
#pragma once

#include <qlib/all.h>
#include <vsg/all.h>

namespace qbe::render {

struct PointMesh {
  vsg::ref_ptr<vsg::vec2Array> vertices;
  vsg::ref_ptr<vsg::uintArray> indices;
  PointMesh(){};
  ~PointMesh(){};
  PointMesh(const qlib::Vec2dArray& points) {
    uint32_t size = points.size();
    vertices = vsg::vec2Array::create(size);
    indices = vsg::uintArray::create(size);
    for (uint32_t i = 0; i < size; i++) {
      (*vertices)[i] = vsg::vec2{float(points[i].x), float(points[i].y)};
      (*indices)[i] = i;
    }
  };
};

struct LineMesh {
  // 线
  vsg::ref_ptr<vsg::vec2Array> vertices;
  vsg::ref_ptr<vsg::uintArray> indices;
  LineMesh(){};
  ~LineMesh(){};
  LineMesh(const qlib::Vec2dArray& points) {
    uint32_t size = points.size();
    vertices = vsg::vec2Array::create(size);
    indices = vsg::uintArray::create(size);
    for (uint32_t i = 0; i < size; i++) {
      (*vertices)[i] = vsg::vec2{float(points[i].x), float(points[i].y)};
      (*indices)[i] = i;
    }
  };
};

struct LineMeshBundle {
  std::vector<LineMesh> meshs;
  LineMeshBundle(){};
  ~LineMeshBundle(){};
  LineMeshBundle(const std::vector<qlib::Vec2dArray>& line_arr) {
    meshs = {};
    for (auto& points : line_arr) {
      meshs.push_back(LineMesh(points));
    }
  };
  LineMeshBundle(const std::vector<LineMesh>& _meshs) { meshs = _meshs; };
};

struct LineListMesh {
  // 线
  vsg::ref_ptr<vsg::vec2Array> vertices;
  vsg::ref_ptr<vsg::uintArray> indices;
  LineListMesh(){};
  ~LineListMesh(){};
  LineListMesh(const qlib::Vec2dArray& points) { init(points); };
  LineListMesh(const std::vector<qlib::Vec2dArray>& line_arr) {
    qlib::Vec2dArray points = {};
    for (auto& line : line_arr) {
      auto line_copy = line;
      if (line_copy.size() > 2) {
        // 遍历并在当前位置后插入元素
        for (size_t i = 1; i < line_copy.size() - 1; ++i) {
          line_copy.insert(line_copy.begin() + i + 1, line_copy[i]);  // 插入当前元素
          ++i;                                                        // 跳过已插入的元素
        }
      }
      points.insert(points.end(), line_copy.begin(), line_copy.end());
    }

    init(points);
  };

  void init(const qlib::Vec2dArray& points) {
    uint32_t size = points.size();
    vertices = vsg::vec2Array::create(size);
    indices = vsg::uintArray::create(size);
    for (uint32_t i = 0; i < size; i++) {
      (*vertices)[i] = vsg::vec2{float(points[i].x), float(points[i].y)};
      (*indices)[i] = i;
    }
  }
};

struct PolyLineMesh {
  // 不填充
  vsg::ref_ptr<vsg::vec2Array> vertices;
  vsg::ref_ptr<vsg::uintArray> indices;
  PolyLineMesh(){};
  ~PolyLineMesh(){};
  PolyLineMesh(const qlib::Vec2dArray& points, bool loop = false) {
    if (loop) {
      init_loop(points);
    } else {
      init(points);
    }
  };

  void init(const qlib::Vec2dArray& points) {
    uint32_t size = points.size();
    vertices = vsg::vec2Array::create(size);
    indices = vsg::uintArray::create(size);
    for (uint32_t i = 0; i < size; i++) {
      (*vertices)[i] = vsg::vec2{float(points[i].x), float(points[i].y)};
      (*indices)[i] = i;
    }
  }

  void init_loop(const qlib::Vec2dArray& points) {
    uint32_t size = points.size();
    vertices = vsg::vec2Array::create(size + 1);
    indices = vsg::uintArray::create(size + 1);
    for (uint32_t i = 0; i < size; i++) {
      (*vertices)[i] = vsg::vec2{float(points[i].x), float(points[i].y)};
      (*indices)[i] = i;
    }
    (*vertices)[size] = vsg::vec2{float(points[0].x), float(points[0].y)};
    (*indices)[size] = size;
  }
};

// 一组
struct PolyLineMeshBundle {
  std::vector<PolyLineMesh> meshs;
  PolyLineMeshBundle(){};
  ~PolyLineMeshBundle(){};
  PolyLineMeshBundle(const std::vector<qlib::Vec2dArray>& points_arr, bool loop = false) {
    meshs = {};
    for (auto& points : points_arr) {
      meshs.push_back(PolyLineMesh(points, loop));
    }
  };
  PolyLineMeshBundle(const qlib::PolygonPtrArray& polys, bool loop = false) {
    meshs = {};
    for (auto& poly : polys) {
      meshs.push_back(PolyLineMesh(poly->points, loop));
    }
  };
};

struct PolyMesh {
  // 带填充
  vsg::ref_ptr<vsg::vec2Array> vertices;
  vsg::ref_ptr<vsg::uintArray> indices;
  vsg::ref_ptr<vsg::uintArray> tri_indices;
  PolyMesh() {}
  ~PolyMesh() {}
  PolyMesh(const qlib::Vec2dArray& points) {
    uint32_t size = points.size();
    vertices = vsg::vec2Array::create(size);
    // indices = vsg::uintArray::create(size);
    for (uint32_t i = 0; i < size; i++) {
      (*vertices)[i] = vsg::vec2{float(points[i].x), float(points[i].y)};
      // (*indices)[i] = i;
    }
    // 三角化
    auto tri_idxs = qlib::tools::tri_earcut(points);
    // tri_indices = vsg::uintArray::create(tri_idxs.size());
    indices = vsg::uintArray::create(tri_idxs.size());
    for (uint32_t i = 0; i < tri_idxs.size(); i++) {
      // (*tri_indices)[i] = tri_idxs[i];
      (*indices)[i] = tri_idxs[i];
    }
  };
};

struct PolyMeshBundle {
  std::vector<PolyMesh> meshs;
  PolyMeshBundle(){};
  ~PolyMeshBundle(){};
  PolyMeshBundle(const std::vector<qlib::Vec2dArray>& points_arr) {
    meshs = {};
    for (auto& points : points_arr) {
      meshs.push_back(PolyMesh(points));
    }
  };
};

struct TextMesh {
  vsg::ref_ptr<vsg::vec2Array> vertices;
  vsg::ref_ptr<vsg::uintArray> indices;
};

struct BoxMesh {
  vsg::ref_ptr<vsg::vec2Array> vertices;
  vsg::ref_ptr<vsg::uintArray> indices;
  BoxMesh(){};
  ~BoxMesh(){};
  BoxMesh(const qlib::Vec2dArray& box) {
    uint32_t size = 5;
    vertices = vsg::vec2Array::create(size);
    indices = vsg::uintArray::create(size);
    auto x = box[0].x;
    auto y = box[0].y;
    auto w = box[1].x - box[0].x;
    auto h = box[1].y - box[0].y;
    vertices->at(0) = vsg::vec2{float(x), float(y)};
    vertices->at(1) = vsg::vec2{float(x + w), float(y)};
    vertices->at(2) = vsg::vec2{float(x + w), float(y + h)};
    vertices->at(3) = vsg::vec2{float(x), float(y + h)};
    vertices->at(4) = vsg::vec2{float(x), float(y)};
    for (uint32_t i = 0; i < size; i++) {
      (*indices)[i] = i;
    }
  };
};

struct BoxMeshBundle {
  std::vector<BoxMesh> meshs;
  BoxMeshBundle(){};
  ~BoxMeshBundle(){};
  BoxMeshBundle(const std::vector<qlib::Vec2dArray>& box_arr) {
    meshs = {};
    for (auto& box : box_arr) {
      meshs.push_back(BoxMesh(box));
    }
  };
};

// struct CellGroup : public vsg::Inherit<vsg::Group, CellGroup> {
struct Mesh2D {
  vsg::ref_ptr<vsg::vec2Array> vertices;
  vsg::ref_ptr<vsg::uintArray> indices;
  std::vector<uint32_t> tri_indices;
  vsg::ref_ptr<vsg::uintArray> triangle_indices;

  bool is_dirty{false};

  Mesh2D(){};
  ~Mesh2D(){};

  Mesh2D(const LineMesh& mesh) {
    vertices = mesh.vertices;
    indices = mesh.indices;
  }

  Mesh2D(const PolyLineMesh& mesh) {
    vertices = mesh.vertices;
    indices = mesh.indices;
  }

  Mesh2D(const LineListMesh& mesh) {
    vertices = mesh.vertices;
    indices = mesh.indices;
  }

  Mesh2D(uint32_t size) {
    vertices = vsg::vec2Array::create(size);
    indices = vsg::uintArray::create(size);
    triangle_indices = vsg::uintArray::create(size);
    vertices->properties.dataVariance = vsg::DYNAMIC_DATA;
  };

  Mesh2D(vsg::ref_ptr<vsg::vec2Array> _vertices) {
    vertices = _vertices;
    indices = vsg::uintArray::create(vertices->size());
    for (uint32_t i = 0; i < vertices->size(); i++) {
      indices->at(i) = i;
    }
    vertices->properties.dataVariance = vsg::DYNAMIC_DATA;
  };

  Mesh2D(vsg::ref_ptr<vsg::vec2Array> _vertices, vsg::ref_ptr<vsg::uintArray> _indices) {
    vertices = _vertices;
    indices = _indices;
    vertices->properties.dataVariance = vsg::DYNAMIC_DATA;
  };

  Mesh2D(qlib::Vec2dArray points, bool connect = false, bool triangle = false) {
    gen_from_points(points, connect);
    if (triangle) {
      gen_tri_indices(points);
    }
    vertices->properties.dataVariance = vsg::DYNAMIC_DATA;
  };

  void gen_from_points(qlib::Vec2dArray points, bool connect = false) {
    uint32_t size = points.size();
    if (size < 1) {
      return;
    }

    uint32_t v_size = size;
    if (connect) {
      v_size += 1;
    }
    vertices = vsg::vec2Array::create(v_size);
    indices = vsg::uintArray::create(v_size);
    for (uint32_t i = 0; i < size; i++) {
      (*vertices)[i] = vsg::vec2{float(points[i].x), float(points[i].y)};
      (*indices)[i] = i;
    }
    // 收尾相连
    if (connect) {
      (*vertices)[size] = vsg::vec2{float(points[0].x), float(points[0].y)};
      (*indices)[size] = size;
    }
  }

  void gen_tri_indices(qlib::Vec2dArray points) { tri_indices = qlib::tools::tri_earcut(points); }

  void update(vsg::ref_ptr<vsg::vec2Array> _vertices) {
    (*vertices) = (*_vertices);
    auto t_indices = vsg::uintArray::create(vertices->size());
    for (uint32_t i = 0; i < vertices->size(); i++) {
      t_indices->at(i) = i;
    }
    (*indices) = (*t_indices);
  }

  void updateVert(vsg::ref_ptr<vsg::vec2Array> _vertices) {
    auto size = vertices->size();
    assert(_vertices->size() < vertices->size());
    for (uint32_t i = 0; i < vertices->size(); i++) {
      if (i < _vertices->size())
        vertices->at(i) = _vertices->at(i);
      else
        vertices->at(i) = _vertices->at(_vertices->size() - 1);
      indices->at(i) = i;
    }
    vertices->dirty();
  }

  void print() {
    SPDLOG_INFO("vertices :");
    for (uint32_t i = 0; i < vertices->size(); i++) {
      auto v = vertices->at(i);
      SPDLOG_INFO("[x,y]: [{}, {}] ", v.x, v.y);
    }
  }

  void dirty() { is_dirty = true; }
};

using Mesh2DArray = std::vector<Mesh2D>;

struct InstanceMesh2D {
  vsg::ref_ptr<vsg::vec3Array> m1;
  vsg::ref_ptr<vsg::vec3Array> m2;
  vsg::ref_ptr<vsg::vec3Array> m3;
  uint32_t count;

  InstanceMesh2D(std::vector<Mat3> mat3_arr) {
    count = mat3_arr.size();
    m1 = vsg::vec3Array::create(count);
    m2 = vsg::vec3Array::create(count);
    m3 = vsg::vec3Array::create(count);
    for (uint32_t i = 0; i < mat3_arr.size(); i++) {
      auto& mat3 = mat3_arr[i];
      (*m1)[i] = mat3[0];
      (*m2)[i] = mat3[1];
      (*m3)[i] = mat3[2];
    }
  }
};

struct LayoutPointMesh {
  qlib::Vec2dArray vertices;
  LayoutPointMesh(){};
  ~LayoutPointMesh(){};
  LayoutPointMesh(const qlib::Vec2dArray& points) { vertices = points; };
};

struct LayoutLineMesh {
  // 线
  qlib::Vec2dArray vertices;
  std::vector<uint32_t> indices;  // 三角化索引
  LayoutLineMesh(){};
  ~LayoutLineMesh(){};
  LayoutLineMesh(const qlib::Vec2dArray& points) { vertices = points; };
};

struct LayoutPolyMesh {
  // 线
  qlib::Vec2dArray vertices;
  std::vector<uint32_t> indices;  // 三角化索引
  LayoutPolyMesh(){};
  ~LayoutPolyMesh(){};
  LayoutPolyMesh(const qlib::Vec2dArray& points, bool triangle = false) {
    vertices = points;
    if (triangle)
      indices = qlib::tools::tri_earcut(points);
  };

  LayoutPolyMesh(const qlib::Polygon::s_ptr& poly, bool triangle = false) {
    vertices = poly->points;
    if (triangle)
      indices = qlib::tools::tri_earcut(poly->points);
  };
};

struct LayoutPolyBundleMesh {
  std::vector<LayoutPolyMesh> meshs;
  LayoutPolyBundleMesh(){};
  ~LayoutPolyBundleMesh(){};
  LayoutPolyBundleMesh(const qlib::PolygonPtrArray& polys, bool triangle = false) {
    meshs = {};
    for (auto& poly : polys) {
      meshs.push_back(LayoutPolyMesh(poly, triangle));
    }
  };
};

}  // namespace qbe::render